package model;

import java.util.ArrayList;
import java.util.TreeMap;

import exceptions.ExecaoHalt;
import exceptions.ExecaoInstrucaoInexistente;
import exceptions.ExecaoMontador;

import basics.ConversaoDeBases;

public class UnidadeDeControle {

	private long pc;

	private TreeMap<String, Registrador> mapaReg;
	private Memoria memoria;
	private ULA ula;
	private Montador montador;

	private TreeMap<String, String> mapaOp;
	private TreeMap<String, String> mapaFunction;

	protected final static int NAN = -1;

	protected UnidadeDeControle(TreeMap<String, Registrador> mapaReg, Memoria memoria, ULA ula) {

		this.mapaReg = mapaReg;
		this.memoria = memoria;
		this.montador = new Montador();
		this.ula = ula;

		mapaOp = new TreeMap<String, String>();
		mapaFunction = new TreeMap<String, String>();

		criaMapas();

		// "program counter" inicia em NAN
		pc = NAN;

	}

	protected ArrayList<LinhaDeExecucao> assemblyToBin(ArrayList<String> assemblyCode) throws ExecaoMontador {
		// Transforma o código assembly em linguagem de máquina e o coloca na
		// memória
		ArrayList<LinhaDeExecucao> listaDeExecucao =
				this.montador.obtemInstrucoes(memoria, assemblyCode);

		// atualiza o pc, atribui a ele o valor inicial da memória onde foi
		// colocado o programa
		pc = montador.getPosicaoMemoria();
		return listaDeExecucao;
	}

	// Executa todas as instruções na memória a partir do PC
	protected void executaTodasInstrucoes() throws ExecaoInstrucaoInexistente, ExecaoHalt {

		long pcAux = pc;
		String pcString = ConversaoDeBases.DecToBin(pcAux);
		String palavraPC;

		// Palavra do PC é o conteúdo da memoria referente ao PC.
		palavraPC = memoria.getWord(pcString);

		// enquanto não acabar a memoria

		while (palavraPC != null) {

			pcAux = pc;
			// executa a instrução propriamente dita, dado o valor de endereço
			// pc

			executaInstrucao(pcString);

			// caso de jump, para evitar pulos redundantes
			if (pcAux == pc)
				incrementaPc();

			pcString = ConversaoDeBases.DecToBin(pc);
			palavraPC = memoria.getWord(pcString);
		}
	}

	// Executa a instrução correspondente ao endereço de memória dado
	protected void executaInstrucao(String endereco) throws ExecaoInstrucaoInexistente, ExecaoHalt {

		// Se houver instrução valida
		String palavraPC;
		

		// Palavra do PC é a concatenação dos 4bytes referentes ao PC.
		palavraPC = memoria.getWord(endereco);

		try {

			if (isHalt(palavraPC))
				throw new ExecaoHalt(ConversaoDeBases.binToDecUnsigned(endereco));

			Instrucao instrucao;
			String[] endInstrucao = new String[4];
			String binaryCode;

			endInstrucao[0] = endereco;
			endInstrucao[1] = ula.somaU(endereco, ConversaoDeBases.DecToBin(1));
			endInstrucao[2] = ula.somaU(endereco, ConversaoDeBases.DecToBin(2));
			endInstrucao[3] = ula.somaU(endereco, ConversaoDeBases.DecToBin(3));

			binaryCode =
					memoria.get(endInstrucao[0]) + memoria.get(endInstrucao[1])
							+ memoria.get(endInstrucao[2])
							+ memoria.get(endInstrucao[3]);

			// atualiza o pc
			pc = ConversaoDeBases.binToDecUnsigned(endereco);

			// Obtem o op da instrução referente ao endereço dado
			String op = binaryCode.substring(0, 6);

			// Tipo R
			if (isTipoR(op)) {
				instrucao = new TipoR();

				instrucao.setOp(op);

				((TipoR) instrucao).setRs(binaryCode.substring(6, 11));
				((TipoR) instrucao).setRt(binaryCode.substring(11, 16));
				((TipoR) instrucao).setRd(binaryCode.substring(16, 21));
				((TipoR) instrucao).setShamt(binaryCode.substring(21, 26));
				((TipoR) instrucao).setFunction(binaryCode.substring(26, 32));

				// Define endereço da instrução
				instrucao.setEndereco(ConversaoDeBases.binToDecUnsigned(endereco));

				executaInstrucao((TipoR) instrucao);

				// executou com sucesso
			}

			// Tipo J
			else if (isTipoJ(op)) {
				instrucao = new TipoJ();

				instrucao.setOp(op);

				((TipoJ) instrucao).setRotuloDeDesvio(binaryCode.substring(6, 32));

				// Define endereço da instrução
				instrucao.setEndereco(ConversaoDeBases.binToDecUnsigned(endereco));

				executaInstrucao((TipoJ) instrucao);
				// executou com sucesso
			}

			// Tipo I
			else if (isTipoI(op)) {
				instrucao = new TipoI();

				instrucao.setOp(op);

				((TipoI) instrucao).setRs(binaryCode.substring(6, 11));
				((TipoI) instrucao).setRt(binaryCode.substring(11, 16));
				((TipoI) instrucao).setImediato(binaryCode.substring(16, 32));

				// Define endereço da instrução
				instrucao.setEndereco(ConversaoDeBases.binToDecUnsigned(endereco));

				executaInstrucao((TipoI) instrucao);
				// executou com sucesso
			}

			
		} catch (ExecaoHalt e) {
			throw e;
		} catch (Exception e) {
			// aqui deve lançar uma exceção
			throw new ExecaoInstrucaoInexistente(pc);
		}
	}

	private void executaInstrucao(TipoR instrucao) {

		String op1 = null;
		String op2 = null;
		String valor = null;
		Registrador destino = null;

		if (mapaFunction.get(instrucao.getFunction()).equals("add")) {

			op1 = mapaReg.get(instrucao.getRs()).getValor();
			op2 = mapaReg.get(instrucao.getRt()).getValor();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.soma(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}

		else if (mapaFunction.get(instrucao.getFunction()).equals("sub")) {

			op1 = mapaReg.get(instrucao.getRs()).getValor();
			op2 = mapaReg.get(instrucao.getRt()).getValor();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.subtracao(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}

		else if (mapaFunction.get(instrucao.getFunction()).equals("and")) {

			op1 = mapaReg.get(instrucao.getRs()).getValor();
			op2 = mapaReg.get(instrucao.getRt()).getValor();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.and(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}

		else if (mapaFunction.get(instrucao.getFunction()).equals("or")) {

			op1 = mapaReg.get(instrucao.getRs()).getValor();
			op2 = mapaReg.get(instrucao.getRt()).getValor();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.or(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}

		else if (mapaFunction.get(instrucao.getFunction()).equals("nor")) {

			op1 = mapaReg.get(instrucao.getRs()).getValor();
			op2 = mapaReg.get(instrucao.getRt()).getValor();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.nor(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}

		else if (mapaFunction.get(instrucao.getFunction()).equals("xor")) {

			// xor $rd, $rs, $rt
			op1 = mapaReg.get(instrucao.getRs()).getValor();
			op2 = mapaReg.get(instrucao.getRt()).getValor();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.xor(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}


		else if (mapaFunction.get(instrucao.getFunction()).equals("sll")) {

			op1 = mapaReg.get(instrucao.getRt()).getValor();
			op2 = instrucao.getShamt();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.shiftLeft(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}

		else if (mapaFunction.get(instrucao.getFunction()).equals("srl")) {

			op1 = mapaReg.get(instrucao.getRt()).getValor();
			op2 = instrucao.getShamt();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.shiftRight(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}


		else if (mapaFunction.get(instrucao.getFunction()).equals("slt")) {

			op1 = mapaReg.get(instrucao.getRs()).getValor();
			op2 = mapaReg.get(instrucao.getRt()).getValor();
			destino = mapaReg.get(instrucao.getRd());

			valor = ula.menor(op1, op2);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			destino.setValor(valor);
		}

		// Instruções do tipo desvio
		else if (mapaFunction.get(instrucao.getFunction()).equals("jr")) {
			// atualiza o valor do pc
			pc =
					ConversaoDeBases.binToDecUnsigned(mapaReg.get(instrucao.getRs()).getValor());
		}
	}

	private void executaInstrucao(TipoI instrucao) {

		String op1 = null;
		String imediato;
		String valor = null;
		String[] endereco = new String[4];
		Registrador regDestino = null;

		if (mapaOp.get(instrucao.getOp()).equals("addi")) {

			op1 = mapaReg.get(instrucao.getRs()).getValor();
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			regDestino = mapaReg.get(instrucao.getRt());


			valor = ula.soma(op1, imediato);
			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("lw")) {

			// obtém o valor contido em $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));

			// registrador de destino: $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// Carrega da memória
			endereco[0] = ula.somaU(op1, imediato);
			endereco[1] = ula.somaU(endereco[0], ConversaoDeBases.DecToBin(1));
			endereco[2] = ula.somaU(endereco[0], ConversaoDeBases.DecToBin(2));
			endereco[3] = ula.somaU(endereco[0], ConversaoDeBases.DecToBin(3));

			valor =
					memoria.get(endereco[0]) + memoria.get(endereco[1])
							+ memoria.get(endereco[2])
							+ memoria.get(endereco[3]);

			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("sw")) {

			// obtém o valor contido em $rs para definir o endereço da memória
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// obtém o valor contido em $rt para gravá-lo na memória
			valor = mapaReg.get(instrucao.getRt()).getValor();

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			
			// obtém os endereços da memória em que o valor deve ser amazenado
			endereco[0] = ula.somaU(op1, imediato);
			endereco[1] = ula.somaU(endereco[0], ConversaoDeBases.DecToBin(1));
			endereco[2] = ula.somaU(endereco[0], ConversaoDeBases.DecToBin(2));
			endereco[3] = ula.somaU(endereco[0], ConversaoDeBases.DecToBin(3));

			// atualiza a memória nos endereços de destino
			memoria.set(endereco[0], valor.substring(0, 8));
			memoria.set(endereco[1], valor.substring(8, 16));
			memoria.set(endereco[2], valor.substring(16, 24));
			memoria.set(endereco[3], valor.substring(24, 32));

		}

		else if (mapaOp.get(instrucao.getOp()).equals("lh")) {

			// lh $rt, i($rs)
			// obtém o valor contido em $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// registrador de destino: $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// Carrega da memória 16bits
			valor = memoria.getHalf(ula.somaU(op1, imediato));

			// Se for negativo, complementa com 16bits 1 para estender o sinal
			if (!ula.igual(ula.menor(valor, ConversaoDeBases.DecToBin(0)), ConversaoDeBases.DecToBin(0)))
				valor = "1111111111111111" + valor;
			else
				valor = "0000000000000000" + valor;

			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("lhu")) {

			// lhu $rt, i($rs)
			// obtém o valor contido em $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// registrador de destino: $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// Carrega da memória 16bits
			valor = memoria.getHalf(ula.somaU(op1, imediato));

			// Complementa o resto do valor, os 16bits mais significativos com
			// "0"
			valor = "0000000000000000" + valor;

			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("sh")) {

			// sh $rt, i($rs)
			// obtém o valor contido em $rs para definir o endereço da memória
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// obtém os 16bits menos significativos contido em $rt para gravá-lo
			// na memória
			valor = mapaReg.get(instrucao.getRt()).getValor().substring(16, 32);

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// obtém os endereços da memória em que o valor deve ser amazenado
			endereco[0] = ula.somaU(op1, imediato);
			endereco[1] = ula.somaU(endereco[0], ConversaoDeBases.DecToBin(1));

			// atualiza a memória nos endereços de destino
			memoria.set(endereco[0], valor.substring(0, 8));
			memoria.set(endereco[1], valor.substring(8, 16));
		}

		else if (mapaOp.get(instrucao.getOp()).equals("lb")) {

			// lb $rt, i($rs)
			// obtém o valor contido em $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// registrador de destino: $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// Carrega da memória os 8bits menos significativos
			endereco[0] = ula.somaU(op1, imediato);

			valor = memoria.get(endereco[0]);

			// Se for negativo, complementa com 24bits 1 para estender o sinal
			if (!ula.igual(ula.menor(valor, ConversaoDeBases.DecToBin(0)), ConversaoDeBases.DecToBin(0)))
				valor = "111111111111111111111111" + valor;
			else
				valor = "000000000000000000000000" + valor;

			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("lbu")) {

			// lbu $rt, i($rs)
			// obtém o valor contido em $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// registrador de destino: $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// Carrega da memória os 8bits menos significativos
			endereco[0] = ula.somaU(op1, imediato);

			valor = memoria.get(endereco[0]);

			// Complementa o resto do valor, os 24bits mais significativos com
			// "0"
			valor = "000000000000000000000000" + valor;

			// atualiza o registrador de destino
			// mapaReg.put(valor, destino);
			// Como o registrador é um objeto, basta atualizar o valor dele, não
			// precisa mudar diretamente o mapa de registradores
			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("sb")) {

			// sb $rt, i($rs)
			// obtém o valor contido em $rs para definir o endereço da memória
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// obtém os 8bits menos significativos contido em $rt para gravá-lo
			// na memória
			valor = mapaReg.get(instrucao.getRt()).getValor().substring(24, 32);

			// Offset
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// obtém os endereços da memória em que o valor deve ser amazenado
			endereco[0] = ula.somaU(op1, imediato);

			// atualiza a memória nos endereços de destino
			memoria.set(endereco[0], valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("lui")) {
			// lui $rt, i
			// o destino é o registrador $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// imediato
			imediato = instrucao.getImediato();

			op1 = imediato + "0000000000000000";

			regDestino.setValor(op1);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("andi")) {
			// andi $rt, rs, i

			// $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// destino $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// imediato
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			valor = ula.and(op1, imediato);

			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("ori")) {
			// ori $rt, rs, i

			// $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// destino $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// imediato
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			valor = ula.or(op1, imediato);

			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("xori")) {
			// ori $rs, rt, i

			// $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// destino $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// imediato
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			valor = ula.xor(op1, imediato);

			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("beq")) {
			// beq $rs, rt, i

			// $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// $rt
			valor = mapaReg.get(instrucao.getRt()).getValor();

			// imediato
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// Se $rs == rt, atualiza o pc para executar o desvio
			if (ula.igual(op1, valor))
				// desvio = imediato + (pc+4)
				pc = ConversaoDeBases.binToDecUnsigned(imediato) + (pc + 4);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("bne")) {
			// bne $rs, rt, i

			// $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// $rt
			valor = mapaReg.get(instrucao.getRt()).getValor();

			// imediato
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// Se $rs != rt, atualiza o pc para executar o desvio
			if (!ula.igual(op1, valor))
				// desvio = imediato + (pc+4)
				pc = ConversaoDeBases.binToDecUnsigned(imediato) + (pc + 4);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("slti")) {
			// slti $rt, rs, i

			// $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// imediato
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// $rs < i
			valor = ula.menor(op1, imediato);

			// atualiza $rt, que é o registrador de destino
			regDestino.setValor(valor);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("sltiu")) {
			// slti $rt, rs, i

			// $rs
			op1 = mapaReg.get(instrucao.getRs()).getValor();

			// $rt
			regDestino = mapaReg.get(instrucao.getRt());

			// imediato
			imediato = ConversaoDeBases.DecToBin(ConversaoDeBases.binToDecSigned(instrucao.getImediato()));
			// $rs < i, desconsiderando o sinal
			valor = ula.menorU(op1, imediato);

			// atualiza $rt, que é o registrador de destino
			regDestino.setValor(valor);
		}

		// Falta ainda os comandos de load linked e store conditional
	}

	private void executaInstrucao(TipoJ instrucao) {
		String desvio = null;
		String destino = null;

		if (mapaOp.get(instrucao.getOp()).equals("j")) {

			desvio = instrucao.getRotuloDeDesvio();

			// destino = desvio << 2, ou seja, destino = desvio*4
			destino =
					ConversaoDeBases.normalizeNumericString(ula.shiftLeft(desvio, ConversaoDeBases.DecToBin(2)), 32);

			// Atualiza o pc para executar o desvio
			pc = ConversaoDeBases.binToDecUnsigned(destino);
		}

		else if (mapaOp.get(instrucao.getOp()).equals("jal")) {

			desvio = instrucao.getRotuloDeDesvio();

			// destino = desvio << 2, ou seja, destino = desvio*4
			destino =
					ConversaoDeBases.normalizeNumericString(ula.shiftLeft(desvio, ConversaoDeBases.DecToBin(2)), 32);

			// atualiza o registrador $ra para pc+4
			mapaReg.get("11111").setValor(ConversaoDeBases.DecToBin(pc + 4));

			// Atualiza o pc para executar o desvio
			pc = ConversaoDeBases.binToDecUnsigned(destino);
		}

	}
	
	protected void incrementaPc(){
		pc += 4;
	}

	protected String getPc() {
		return ConversaoDeBases.DecToBin(pc);
	}

	private boolean isTipoR(String op) {
		return op.equals("000000");
	}

	private boolean isTipoJ(String op) {
		return (op.equals("000010") || (op.equals("000011")));
	}

	private boolean isTipoI(String op) {
		return (op.equals("001000") || (op.equals("001001"))
				|| (op.equals("001100")) || (op.equals("000100"))
				|| (op.equals("000001")) || (op.equals("000111"))
				|| (op.equals("000110")) || (op.equals("000001"))
				|| (op.equals("000101")) || (op.equals("100000"))
				|| (op.equals("100100")) || (op.equals("100001"))
				|| (op.equals("100101")) || (op.equals("001111"))
				|| (op.equals("100011")) || (op.equals("110001"))
				|| (op.equals("001101")) || (op.equals("101000"))
				|| (op.equals("001010")) || (op.equals("001011"))
				|| (op.equals("101001")) || (op.equals("101011"))
				|| (op.equals("111001")) || (op.equals("001110")));
	}

	private boolean isHalt(String palavra) {

		String op = palavra.substring(0, 6);
		String function = palavra.substring(26, 32);

		// se é do tipo R e a function é halt
		return ((isTipoR(op)) && (function.equals("111111")));

	}

	public void criaMapas() {
		criaMapaOp();
		criaMapaFunction();
	}

	public void criaMapaOp() {

		// Tipo I
		mapaOp.put("001000", "addi");
		mapaOp.put("001001", "addiu");
		mapaOp.put("001100", "andi");
		mapaOp.put("000100", "beq");
		mapaOp.put("000001", "bgez");
		mapaOp.put("000111", "bgtz");
		mapaOp.put("000110", "blez");
		mapaOp.put("000001", "bltz");
		mapaOp.put("000101", "bne");
		mapaOp.put("100000", "lb");
		mapaOp.put("100100", "lbu");
		mapaOp.put("100001", "lh");
		mapaOp.put("100101", "lhu");
		mapaOp.put("001111", "lui");
		mapaOp.put("100011", "lw");
		mapaOp.put("110001", "lwcl");
		mapaOp.put("001101", "ori");
		mapaOp.put("101000", "sb");
		mapaOp.put("001010", "slti");
		mapaOp.put("001011", "sltiu");
		mapaOp.put("101001", "sh");
		mapaOp.put("101011", "sw");
		mapaOp.put("111001", "swcl");
		mapaOp.put("001110", "xori");

		// Tipo J
		mapaOp.put("000010", "j");
		mapaOp.put("000011", "jal");

	}

	public void criaMapaFunction() {

		mapaFunction.put("100000", "add");
		mapaFunction.put("100001", "addu");
		mapaFunction.put("100100", "and");
		mapaFunction.put("001101", "break");
		mapaFunction.put("011010", "div");
		mapaFunction.put("011011", "divu");
		mapaFunction.put("001001", "jalr");
		mapaFunction.put("001000", "jr");
		mapaFunction.put("010000", "mfhi");
		mapaFunction.put("010010", "mflo");
		mapaFunction.put("010001", "mthi");
		mapaFunction.put("010011", "mtlo");
		mapaFunction.put("011000", "mult");
		mapaFunction.put("011001", "multu");
		mapaFunction.put("100111", "nor");
		mapaFunction.put("100101", "or");
		mapaFunction.put("000000", "sll");
		mapaFunction.put("000100", "sllv");
		mapaFunction.put("101010", "slt");
		mapaFunction.put("101011", "sltu");
		mapaFunction.put("000011", "sra");
		mapaFunction.put("000111", "srav");
		mapaFunction.put("000010", "srl");
		mapaFunction.put("000110", "srlv");
		mapaFunction.put("100010", "sub");
		mapaFunction.put("100011", "subu");
		mapaFunction.put("001100", "syscall");
		mapaFunction.put("100110", "xor");

		// Halt
		mapaFunction.put("111111", "halt");

	}

}
